
import com.kirillam.handy.core.HATControlClip;
import com.kirillam.handy.core.HATObject;
import com.kirillam.handy.events.HATEvent;


/** 
 * This class is a hack to make something like the Dictionary class in AS3 that has weak keys.
 * When calling setInterval with an object and a function name it doesn't seem to store a reference
 * to the object anywhere. If it does it doesn't update the reference count anyway and the object gets
 * garbage collected if you don't store it yourself. Also from testing it looks like if you pass 1 as
 * the interval then the first thing that runs on the next frame besides the code on the frames is the
 * interval function. So this class accomplishes something like weak keys by allowing a single object
 * per frame to be garbage collected by setting an interval for it that puts the object back first thing
 * on the next frame. It scans through all WeakReference objects periodically testing each one by one
 * so not to take away from performance.
 */

class com.kirillam.handy.datastructures.WeakReference extends HATObject
{
	/* Queue of all WeakReferenceobjects */
	private static var queue	: Array		= new Array();
	
	/** 
	 * Puts a WeakReference object on the queue.
	 * 
	 * @param	r	The WeakReference object to store.
	 */
	
	private static function putOnQueue( r : WeakReference )
	{
		r.isOnQueue	= true;
		queue.push( r );
	}
	
	
	/*
	 * Handles the ForWeakReferences event from the control clip. Dequeues a reference
	 * and puts it back at the end of the queue. Sets up an interval for the object to
	 * store it again and removes the reference to it allowing it to be garbage collected.
	 */
	
	public static function onAboutToRender()
	{
		if( queue.length == 0 ) return;
		
		// First find a reference that has an object.
		var ref		: Object	= queue.shift();
		while( ref.object == null && queue.length > 0 ) {
			ref.isOnQueue		= false;
			ref					= queue.shift();
		}

		if( ref.object == null ) return;
		
		
		
		// Set up the interval function on the object that puts it back into the reference.
		var fnName	: String	= "__HAT__WeakReference";
		ref.object[ fnName ]	= function()
		{
			clearInterval( arguments.callee.id );
			ref.object			= this;

			delete this[ fnName ];
		};

		ref.object[ fnName ].id	= setInterval( ref.object, fnName, 1 );
		checkLastReference.id	= setInterval( checkLastReference, 1, WeakReference );
		
		// Remove the reference to the object.
		ref.object				= null;
		queue.push( ref );
	}
	
	
	/* Interval function that checks if the object was destroyed and makes the reference send and event about it. */
	
	private static function checkLastReference( wr : Object )
	{
		clearInterval( arguments.callee.id );

		if( wr.queue[ wr.queue.length - 1 ].object == null ) {
			wr.queue.pop().informRemoved();
		}
	}
	
	
	
	/* Flag that tells that this reference is in the queue. */
	private var isOnQueue	: Boolean	= false;
	
	
	private var _object		: Object;
	
	/** Points to an object to store a reference to. */
	
	public function set object( o : Object )
	{
		_object			= o;
		
		if( o != null ) {
			
			_objectId	= o.hatId();
			if( !isOnQueue ) {
				putOnQueue( this );
			}
		}
	}
	
	public function get object() : Object
	{
		return _object;
	}
	
	
	private var _objectId	: Number;
	
	/** Retrieves the hatId of the object referenced. */
	
	public function get objectId() : Number
	{
		return _objectId;
	}
	
	
	
	/**  Constructor */
	
	public function WeakReference( o : Object )
	{
		super();
		
		if( o != null ) {
			object = o;
		}
	}
	
	
	/** Sends out a HATEvent.REMOVED event. */
	
	private function informRemoved()
	{
		sendEvent( new HATEvent( HATEvent.REMOVED ) );
	}
}